﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MetroIoc;
using Microsoft.Practices.Prism;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.Logging;
using Microsoft.Practices.Prism.Modularity;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.ServiceLocation;

namespace Prism.Extensions.MetroIOC
{
    public abstract class MetroBootstrapper : Bootstrapper
    {
        private bool useDefaultConfiguration = true;

        public IContainer Container { get; protected set; }

        public override void Run(bool runWithDefaultConfiguration)
        {
           this.Logger = this.CreateLogger();
             if (this.Logger == null)
            {
                throw new InvalidOperationException(ResourceHelper.NullLoggerFacadeException);
            }
            this.Logger.Log(ResourceHelper.LoggerCreatedSuccessfully, Category.Debug, Priority.Low);
            this.Logger.Log(ResourceHelper.CreatingModuleCatalog, Category.Debug, Priority.Low);

            this.ModuleCatalog = this.CreateModuleCatalog();
            if (this.ModuleCatalog == null)
            {
                throw new InvalidOperationException(ResourceHelper.NullModuleCatalogException);
            }

            this.Logger.Log(ResourceHelper.ConfiguringModuleCatalog, Category.Debug, Priority.Low);
            this.ConfigureModuleCatalog();


            this.Logger.Log(ResourceHelper.CreatingUnityContainer, Category.Debug, Priority.Low);
            this.Container = this.CreateContainer();
            if (this.Container == null)
            {
                throw new InvalidOperationException(ResourceHelper.NullUnityContainerException);
            }

            this.Logger.Log(ResourceHelper.ConfiguringUnityContainer, Category.Debug, Priority.Low);
            this.ConfigureContainer();

            this.Logger.Log(ResourceHelper.ConfiguringServiceLocatorSingleton, Category.Debug, Priority.Low);
            this.ConfigureServiceLocator();

            this.Logger.Log(ResourceHelper.ConfiguringRegionAdapters, Category.Debug, Priority.Low);
            this.ConfigureRegionAdapterMappings();

            this.Logger.Log(ResourceHelper.ConfiguringDefaultRegionBehaviors, Category.Debug, Priority.Low);
            this.ConfigureDefaultRegionBehaviors();

            this.Logger.Log(ResourceHelper.RegisteringFrameworkExceptionTypes, Category.Debug, Priority.Low);
            this.RegisterFrameworkExceptionTypes();

            this.Logger.Log(ResourceHelper.CreatingShell, Category.Debug, Priority.Low);
            this.Shell = this.CreateShell();

            if (this.Shell != null)
            {
                this.Logger.Log(ResourceHelper.SettingTheRegionManager, Category.Debug, Priority.Low);
                RegionManager.SetRegionManager(this.Shell, this.Container.Resolve<IRegionManager>());

                this.Logger.Log(ResourceHelper.UpdatingRegions, Category.Debug, Priority.Low);
                RegionManager.UpdateRegions();

                this.Logger.Log(ResourceHelper.InitializingShell, Category.Debug, Priority.Low);
                this.InitializeShell();
            }
               
            
            this.Logger.Log(ResourceHelper.InitializingModules, Category.Debug, Priority.Low);
            this.InitializeModules();
            

            this.Logger.Log(ResourceHelper.BootstrapperSequenceCompleted, Category.Debug, Priority.Low);
        }
       

        protected override void ConfigureServiceLocator()
        {            
            ServiceLocator.SetLocatorProvider(() => this.Container.Resolve<IServiceLocator>());        
        }


        /// <summary>
        /// Creates the <see cref="IUnityContainer"/> that will be used as the default container.
        /// </summary>
        /// <returns>A new instance of <see cref="IUnityContainer"/>.</returns>        
        protected virtual IContainer CreateContainer()
        {

            return new MetroContainer();
        }


        

        /// <summary>
        /// Configures the <see cref="IUnityContainer"/>. May be overwritten in a derived class to add specific
        /// type mappings required by the application.
        /// </summary>
        protected virtual void ConfigureContainer()
        {
            this.Logger.Log(ResourceHelper.AddingUnityBootstrapperExtensionToContainer, Category.Debug, Priority.Low);
            //this.Container.AddNewExtension<UnityBootstrapperExtension>();
                        
            
            Container.RegisterInstance<ILoggerFacade>(Logger);

            this.Container.RegisterInstance(this.ModuleCatalog);
             MetroIocServiceLocatorAdapter serviceLocator = new MetroIocServiceLocatorAdapter(Container);
             Container.RegisterInstance<IServiceLocator>(serviceLocator);
             Container.RegisterInstance<IContainer>(Container);


            if (useDefaultConfiguration)
            {
                RegisterType<IModuleInitializer, ModuleInitializer>(true);
                RegisterType<IModuleManager, ModuleManager>(true);
                RegisterType<RegionAdapterMappings, RegionAdapterMappings>(true);
                RegisterType<IRegionManager, RegionManager>(true);
                RegisterType<IEventAggregator, EventAggregator>(true);
                RegisterType<IRegionViewRegistry, RegionViewRegistry>(true);
                RegisterType<IRegionBehaviorFactory, RegionBehaviorFactory>(true);
                RegisterType<IRegionNavigationJournalEntry, RegionNavigationJournalEntry>(false);
                RegisterType<IRegionNavigationJournal, RegionNavigationJournal>(false);
                RegisterType<IRegionNavigationService, RegionNavigationService>(false);
                RegisterType<IRegionNavigationContentLoader, RegionNavigationContentLoader>(false);
            }
        }
        /// <summary>
        /// Initializes the modules. May be overwritten in a derived class to use a custom Modules Catalog
        /// </summary>
        protected override void InitializeModules()
        {
            IModuleManager manager;
            manager = this.Container.Resolve<IModuleManager>();
            manager.Run();
        }

        /// <summary>
        /// Registers a type in the container only if that type was not already registered.
        /// </summary>
        /// <param name="fromType">The interface type to register.</param>
        /// <param name="toType">The type implementing the interface.</param>
        /// <param name="registerAsSingleton">Registers the type as a singleton.</param>
        protected void RegisterType<TFrom, TTo>(bool registerAsSingleton) where TTo : TFrom
        {
            if (registerAsSingleton)
            {
                Container.Register<TFrom, TTo>(null, new SingletonLifecycle());
            }
            else
            {
                Container.Register<TFrom, TTo>();
            }
        }


    }
}
